package com.ymt.bpm.appboot;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.context.event.ApplicationPreparedEvent;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.ContextStartedEvent;
import org.springframework.context.event.ContextStoppedEvent;

import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.Set;

/**
 * Created by Johnny on 2016/12/23.
 * Spring appboot application status listener
 */
public class ApplicationStartupListener implements ApplicationListener {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * Spring appboot application status listener
     * @param event
     */
    @Override
    public void onApplicationEvent(ApplicationEvent event) {

        ApplicationContext appContext = null;
        if (event instanceof ApplicationReadyEvent) {
            logger.info("ApplicationStartupListner applicationEvent:" + event);
            appContext = ((ApplicationReadyEvent) event).getApplicationContext();
        } else {
            return;
        }

        SpringContext.setAppContext(appContext);

        //EngineInitializer for engine initialization
        EngineInitializer initializer = null;
        try {
            initializer = appContext.getBean(EngineInitializer.class);
        } catch (NoSuchBeanDefinitionException e) {
            //do nothing
        }

        if (event instanceof ApplicationEnvironmentPreparedEvent) { // 初始化环境变量
            logger.info("ApplicationEnvironmentPreparedEvent.");
        } else if (event instanceof ApplicationPreparedEvent) { // 初始化完成
            logger.info("ApplicationPreparedEvent.");
        } else if (event instanceof ContextRefreshedEvent) { // 应用刷新
            logger.info("ContextRefreshedEvent.");
            if (initializer!=null) initializer.refresh();
        } else if (event instanceof ApplicationReadyEvent) {// 应用已启动完成
            logger.info("ApplicationReadyEvent.");
            if (initializer!=null) initializer.init();
            printBeans(appContext);
        } else if (event instanceof ContextStartedEvent) { // 应用启动，需要在代码动态添加监听器才可捕获
            logger.info("ContextStartedEvent.");
        } else if (event instanceof ContextStoppedEvent) { // 应用停止
            logger.info("ContextStoppedEvent.");
            if (initializer!=null) initializer.stop();
            onStop();
        } else if (event instanceof ContextClosedEvent) { // 应用关闭
            logger.info("ContextClosedEvent.");
        }
    }

    private void printBeans(ApplicationContext appContext) {
        String[] beanNames =  appContext.getBeanDefinitionNames();
        logger.info("Number of all beanNames："+beanNames.length);
        for(String bn:beanNames){
            logger.info("BeanDefinitionName= "+bn + " : " + appContext.getBean(bn).getClass().getName());
        }
    }

    private void onStop() {
        logger.info("stop...");

        logger.info("Deregistering jdbc driver.");
        Enumeration<Driver> drivers = DriverManager.getDrivers();
        while (drivers.hasMoreElements()) {
            Driver driver = drivers.nextElement();
            try {
                DriverManager.deregisterDriver(driver);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        logger.info("Closing all database connections.");
        destroyThreads();
        logger.info("All db threads stopped.");
    }

    private void destroyThreads(){
        final Set<Thread> threads = Thread.getAllStackTraces().keySet();
        for (Thread thread : threads) {
            if(thread.getName().equals("HouseKeeper")){
                synchronized (this) {
                    try {
                        thread.stop();
                        return;
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}
